/*
 * Galaxium Messenger
 * 
 * Copyright (C) 2007 Ben Motmans <ben.motmans@gmail.com>
 * Copyright (C) 2007 Philippe Durand <draekz@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.Collections.Generic;

using G=Gtk;
using Gtk;
using Glade;

using Anculus.Core;
using Anculus.Gui;

using Galaxium.Core;
using Galaxium.Protocol;
using Galaxium.Protocol.Gui;
using Galaxium.Gui;

namespace Galaxium.Gui.GtkGui
{
	public class CustomAccountEntry
	{
		private string _name;
		private Widget _widget;
		
		public string Name
		{
			get { return _name; }
		}
		
		public Widget Widget
		{
			get { return _widget; }
		}
		
		public CustomAccountEntry (string name, Widget widget)
		{
			_name = name;
			_widget = widget;
		}
	}
	
	public abstract class BasicAccountWidget : AbstractAccountWidget
	{
		protected Table _accountTable = null;
		protected Entry _passwordEntry = null;
		protected CheckButton _autoconnectCheck = null;
		protected CheckButton _rememberPassCheck = null;
		protected Button _connectButton = null;
		protected Button _cancelButton = null;
		protected ProgressBar _progressBar = null;
		protected Button _removeButton = null;
		protected Button _settingsButton = null;
		protected ImageComboBox<IPresence> _presenceCombo = null;
		
		private bool _omitPresenceCombo = false;
		private bool _passwordRequired = true;
		
		private IPresence _initialPresence;
		private List<CustomAccountEntry> _customEntries;
		
		public bool OmitPresenceCombo
		{
			get { return _omitPresenceCombo; }
			set { _omitPresenceCombo = value; }
		}
		
		public bool PasswordRequired
		{
			get { return _passwordRequired; }
			set { _passwordRequired = value; }
		}
		
		public BasicAccountWidget (IProtocol protocol) : base (protocol)
		{
			_customEntries = new List<CustomAccountEntry> ();
			
			_accountTable = new G.Table (3, 3, false);
			_accountTable.RowSpacing = 3;
			_accountTable.ColumnSpacing = 3;
			_accountTable.WidthRequest = 100;
			
			_removeButton = new G.Button ();
			_removeButton.Label = String.Empty;
			_removeButton.Image = new G.Image (IconUtility.GetIcon ("galaxium-remove", IconSizes.Small));
			_removeButton.Clicked += RemoveButtonClicked;
			_removeButton.TooltipText = GettextCatalog.GetString ("Remove Account");
			
			_passwordEntry = new G.Entry ();
			_passwordEntry.Visibility = false;
			_passwordEntry.Changed += PasswordChanged;
			_passwordEntry.ActivatesDefault = true;
			
			_connectButton = new Button ();
			_connectButton.Label = GettextCatalog.GetString ("Connect");
			_connectButton.Image = new Image (IconUtility.GetIcon ("galaxium-ok", IconSizes.Small));
			_connectButton.Clicked += ConnectButtonClicked;
			
			_cancelButton = new G.Button ();
			_cancelButton.Label = GettextCatalog.GetString ("Cancel");
			_cancelButton.Image = new Image (IconUtility.GetIcon ("galaxium-cancel", IconSizes.Small));
			_cancelButton.Clicked += CancelButtonClicked;
			
			_settingsButton = new G.Button ();
			_settingsButton.Label = String.Empty;
			_settingsButton.Image = new G.Image (IconUtility.GetIcon ("galaxium-preferences", IconSizes.Small));
			_settingsButton.Clicked += SettingsButtonClicked;
			_settingsButton.TooltipText = GettextCatalog.GetString ("Connection Settings");
			
			_autoconnectCheck = new G.CheckButton (GettextCatalog.GetString ("Automatically Connect"));
			_rememberPassCheck = new G.CheckButton (GettextCatalog.GetString ("Remember Password"));
			
			_progressBar = new G.ProgressBar();
			_progressBar.Text = " ";
		}
		
		public override void Initialize ()
		{
			base.Initialize ();
			
			Label account_label = new Label (GettextCatalog.GetString ("Account:"));
			_layout_widget = account_label;
			account_label.Xalign = 0.0f;
			
			// Setup the account combo
			_accountCombo = CreateAccountCombo ();
			_accountCombo.Entry.ActivatesDefault = true;
			
			HBox account_box = new G.HBox ();
			account_box.PackStart (_accountCombo, true, true, 0);
			account_box.PackStart (_removeButton, false, true, 0);
			
			Label pass_label = new G.Label (GettextCatalog.GetString ("Password:"));
			pass_label.Xalign = 0.0f;
			
			// Attach everything to the table
			uint pos = 0;
			
			_accountTable.Attach (account_label, 0, 1, pos, pos+1, AttachOptions.Fill, AttachOptions.Fill, 0, 0);
			_accountTable.Attach (account_box, 1, 3, pos, pos+1, AttachOptions.Expand | AttachOptions.Fill | AttachOptions.Shrink, AttachOptions.Fill, 0, 0);
			
			_accountTable.Attach (pass_label, 0, 1, ++pos, pos+1, AttachOptions.Fill, AttachOptions.Fill, 0, 0);
			_accountTable.Attach (_passwordEntry, 1, 3, pos, pos+1, AttachOptions.Expand | AttachOptions.Fill | AttachOptions.Shrink, AttachOptions.Fill, 0, 0);
			
			if (!OmitPresenceCombo)
			{
				Label presence_label = new G.Label (GettextCatalog.GetString ("Presence:"));
				presence_label.Xalign = 0.0f;
				
				_accountTable.Attach (presence_label, 0, 1, ++pos, pos+1, AttachOptions.Fill, AttachOptions.Fill, 0, 0);
				_accountTable.Attach (_presenceCombo, 1, 3, pos, pos+1, AttachOptions.Expand | AttachOptions.Fill | AttachOptions.Shrink, AttachOptions.Fill, 0, 0);
			}
			
			// Here I would let the custom entries be added.
			foreach (CustomAccountEntry entry in _customEntries)
			{
				Label custom_label = new G.Label (entry.Name);
				custom_label.Xalign = 0.0f;
				
				_accountTable.Attach (custom_label, 0, 1, ++pos, pos+1, AttachOptions.Fill, AttachOptions.Fill, 0, 0);
				_accountTable.Attach (entry.Widget, 1, 3, pos, pos+1, AttachOptions.Expand | AttachOptions.Fill | AttachOptions.Shrink, AttachOptions.Fill, 0, 0);
			}
			
			// Organize the buttons
			HBox buttonBox = new G.HBox ();
			buttonBox.PackStart (_connectButton, true, true, 0);
			buttonBox.PackStart (_cancelButton, true, true, 0);
			buttonBox.PackStart (_settingsButton, false, true, 0);
			
			_accountTable.Attach (buttonBox, 1, 3, ++pos, pos+1, AttachOptions.Expand | AttachOptions.Fill | AttachOptions.Shrink, AttachOptions.Fill, 0, 0);
			_accountTable.Attach (_rememberPassCheck, 1, 3, ++pos, pos+1, AttachOptions.Expand | AttachOptions.Fill | AttachOptions.Shrink, AttachOptions.Fill, 0, 0);
			_accountTable.Attach (_autoconnectCheck, 1, 3, ++pos, pos+1, AttachOptions.Expand | AttachOptions.Fill | AttachOptions.Shrink, AttachOptions.Fill, 0, 0);
			
			_accountCombo.FocusChain = new G.Widget[] { _accountCombo.Entry };
			
			account_box.FocusChain = new G.Widget[] { _accountCombo };
			
			List<Widget> widgetChain = new List<Widget> ();
			widgetChain.Add (account_box);
			widgetChain.Add (_passwordEntry);
			if (!OmitPresenceCombo) widgetChain.Add (_presenceCombo);
			widgetChain.Add (_connectButton);
			widgetChain.Add (_cancelButton);
			widgetChain.Add (_rememberPassCheck);
			widgetChain.Add (_autoconnectCheck);
			
			_accountTable.FocusChain = widgetChain.ToArray ();
			
			SetAccountWidget (AccountWidgetPositions.Content, _accountTable, true, true, 0);
			SetAccountWidget (AccountWidgetPositions.Progress, _progressBar, true, true, 0);
			
			base.PopulateFields ();
			
			//_connectButton.Sensitive = CheckInput ();
			
			_nativeWidget.ShowAll ();
		}
		
		public override void Connect ()
		{
			// This is mostly to allow auto-connect to start the process
			ConnectButtonClicked (this, new EventArgs ());
		}
		
		public void AddCustomAccountItem (string name, Widget widget)
		{
			_customEntries.Add (new CustomAccountEntry (name, widget));
		}
		
		public override void ApplyParentLayout (Widget parent)
		{
			base.ApplyParentLayout (parent);
			
			// Setup defaults so that we can hit ENTER on password field
			_connectButton.CanDefault = true;
			_connectButton.HasDefault = true;
		}
		
		protected virtual void LoadAccountInfo ()
		{
			IAccount account = AccountUtility.GetAccount(_protocol, _accountCombo.Entry.Text);
			
			// Here we load all the basic account details into the UI.
			
			if (account != null)
			{
				_passwordEntry.Text = account.Password;
				_autoconnectCheck.Active = account.AutoConnect;
				
				if (!OmitPresenceCombo)
				{
					if (account.InitialPresence != null)
						_presenceCombo.Select (account.InitialPresence);
					else
						Log.Warn ("Account's initial presence is not available: "+account.UniqueIdentifier);
				}
				
				_rememberPassCheck.Active = account.RememberPassword;
			}
			else
			{
				_passwordEntry.Text = String.Empty;
				_autoconnectCheck.Active = false;
				_rememberPassCheck.Active = false;
				if (!OmitPresenceCombo) _presenceCombo.SelectFirst ();
			}
			
			Account = account;
		}
		
		protected IPresence GetInitialPresence ()
		{
			TreeIter iter;
			
			if (_presenceCombo.GetActiveIter (out iter))
				_initialPresence = _presenceCombo.Model.GetValue (iter, 0) as IPresence;
			else
				_initialPresence = UnknownPresence.Instance;
			
			return _initialPresence;
		}
		
		protected override void UpdateProgress (string message, double percent)
		{
			ThrowUtility.ThrowIfNull ("message", message);
			
			// This will stop a disconnect from overwriting an error message
			// I'm not sure it's the best way to handle it though
			bool hasFraction = true;
			bool hasPercentage = true;
			bool hasExistingMessage = true;
			bool hasNewMessage = true;
			
			if (_progressBar.Fraction == 0)
				hasFraction = false;
			
			if (percent == 0)
				hasPercentage = false;
			
			if (string.IsNullOrEmpty (_progressBar.Text.Trim ()))
				hasExistingMessage = false;
			
			if (string.IsNullOrEmpty (message.Trim ()))
				hasNewMessage = false;
			
			if (!hasFraction && !hasPercentage && hasExistingMessage && !hasNewMessage)
				return;
			
			_progressBar.Text = message;
			_progressBar.Fraction = percent;
		}
		
		protected override IAccount SetAccount ()
		{
			Log.Warn ("Protocol account widget does not properly override IAccount SetAccount ()!");
			
			return null;
		}
		
		public override void EnableFields ()
		{
			OnEnableChange (EventArgs.Empty);
			
			if (_accountCombo.Entry.Text.Length < 1)
				_connectButton.Sensitive = false;
			else
				_connectButton.Sensitive = true;
			
			if (SessionUtility.Count >= 1)
				_cancelButton.Sensitive = true;
			else
				_cancelButton.Sensitive = false;
			
			_rememberPassCheck.Sensitive = true;
			_autoconnectCheck.Sensitive = true;
			_passwordEntry.Sensitive = true;
			_accountCombo.Sensitive = true;
			if (!OmitPresenceCombo) _presenceCombo.Sensitive = true;
			_removeButton.Sensitive = true;
			_settingsButton.Sensitive = true;
		}
		
		public override void DisableFields (bool omit_cancel)
		{
			OnDisableChange (EventArgs.Empty);
			
			_connectButton.Sensitive = false;
			_cancelButton.Sensitive = omit_cancel;
			
			_rememberPassCheck.Sensitive = false;
			_autoconnectCheck.Sensitive = false;
			_passwordEntry.Sensitive = false;
			_accountCombo.Sensitive = false;
			if (!OmitPresenceCombo) _presenceCombo.Sensitive = false;
			_removeButton.Sensitive = false;
			_settingsButton.Sensitive = false;
		}
		
		protected virtual bool CheckInput ()
		{
			if ((_passwordEntry.Text.Length == 0 && PasswordRequired) || _accountCombo.Entry.Text.Length == 0)
				return false;
			
			return true;
		}
		
		protected abstract void SettingsButtonClicked (object sender, EventArgs args);
		
		protected virtual void ConnectButtonClicked (object sender, EventArgs args)
		{
			if (_current_session == null)
			{
				Log.Warn ("Protocol account widget failed to create a usable session!");
				return;
			}
			
			DisableFields (true);
			
			_connecting = true;

			_current_session.Connect ();
		}
		
		protected virtual void CancelButtonClicked (object sender, EventArgs args)
		{
			if (_current_session != null && _connecting)
				_current_session.Disconnect ();
			
			CancelLogin ();
		}
		
		protected virtual void RemoveButtonClicked (object sender, EventArgs args)
		{
			RemoveSelectedAccount ();
		}
		
		protected virtual void RememberPasswordToggled (object sender, EventArgs args)
		{
			
		}
		
		protected virtual void AutoConnectToggled (object sender, EventArgs args)
		{
			
		}
		
		protected virtual void PasswordChanged (object sender, EventArgs args)
		{
			_connectButton.Sensitive = CheckInput ();
		}
		
		protected override void AccountNameChanged (object sender, EventArgs args)
		{
			LoadAccountInfo ();
		}
		
		protected override void SessionDisconnected (object sender, SessionEventArgs args)
		{
			EnableFields ();
			
			base.SessionDisconnected (sender, args);
		}
		
		protected override void ErrorOccurred (string error)
		{
			UpdateProgress (error, 0);
			
			EnableFields ();
		}
	}
}